#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/select.h>
#include <sys/inotify.h>
#include <unistd.h>
#include <string>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <iostream>
#include <signal.h>
#include <pthread.h>

// subscriber-------> proxy, save the subscriber  title:subscribe
// proxy------------> monitor, get control event, start monitor video file process
//monitor file process ------------> proxy, save the descriptor, receive the data file
//proxy --------> subcriber, redirect the received data
#define PORT 2627
//#define SERVER_IP "localhost"
//#define STATUS "server" //server or client

#define EVENT_SIZE  ( sizeof (struct inotify_event) )
#define BUF_LEN     ( 1024 * ( EVENT_SIZE + 16 ) )
//put this program in the same path as sserver or ppeer
//for this program, we use ssh tunnel to reflect localport:2627 to wan.poly.edu:2627
//so whenever we send data to localport 2627, wan.poly.edu can get our data.
//BTW: you must run ssh command on the server first.
//ssh -f username@ip -L localport:remoteserverip:remoteport -N
int fd,sd;//file descriptor, socket descriptor
char hostname[255];
FILE *fp;//for file operation
FILE *fpv;
fpos_t pos1,pos2;//record the position
fpos_t pos1v,pos2v;
int wd;//watch descriptor
int isStart;
char *filename;
char videoFileName[50];
int videoIsOn;
char filenameArray[80];

union title_length
{
	unsigned int length;
	char byte[2];
}theLength;
const char *programName;
pid_t cld_pid;

void usage()
{
	std::cout<<"usage: "<<programName<<" <status:server|peer> <IP:the ip of proxy> <uprate> <downrate>\n";
	exit(1);
}

char *getFileName()
{
	//char path[300];
	//getcwd(path,300);//the path
	gethostname(hostname,255);
	printf("host name is: %s\n", hostname);
	sprintf(filenameArray,"%s_rep.txt",hostname);//filename = path/hostname_rep.txt
	return filenameArray;
}


pid_t startServer(char* uprate, char* downrate)
{
	pid_t cld_pid;
	if((cld_pid = fork()) == 0) execl("/usr/bin/trickle", "trickle", "-u",uprate, "-d", downrate, "/home/poly_ke/P2Pserver/sserver",(char*)0);
	else return cld_pid;
}
pid_t startPeer(char* uprate, char* downrate)
{
	pid_t cld_pid;
	if((cld_pid = fork()) == 0) execl("/usr/bin/trickle", "trickle", "-u",uprate, "-d", downrate, "/home/poly_ke/P2Pclient/rpeer",(char*)0);
	else return cld_pid;
}


//start the process for monitoring video
void *startVideoMonitor(void *)
{
	//char path[300];
	//getcwd(path,300);
	printf("thread is open!!!!!!!!!\n");
	//sprintf(videoFileName,"clip.dat");
	fpv=fopen("recordvideo.dat","r+");
	pos2v.__pos = ftell(fpv);//record the start
	fseek(fpv,0,SEEK_END);
	pos1v.__pos = ftell(fpv);//record the current end which is also the length of the file
	char videoBuf[BUF_LEN];
	char sendBuf[BUF_LEN];
	int n;

	//establish a new connection to proxy to send the information
	int sdv;
	sdv = socket(AF_INET,SOCK_STREAM,0);
	if(sdv < 0) {perror("create video socket error!\n");exit(1);}
	struct sockaddr_in proxy_addr;
	proxy_addr.sin_family = AF_INET;
	proxy_addr.sin_port = htons(2628);
	proxy_addr.sin_addr.s_addr=inet_addr("127.0.0.1");
	if (connect(sdv, (struct sockaddr *)&proxy_addr, sizeof(proxy_addr)) <0 ){perror("connect error!(video)\n");exit(1);}

	//first inform proxy it is for sending video
	snprintf(videoBuf,sizeof(videoBuf),"video\r\n");
	if(send(sdv,videoBuf,strlen(videoBuf),0)<0){perror("send");exit(1);}

	//then
	fseek(fpv,0,SEEK_SET);
	if(pos1v.__pos > 2*1024*1024)
	{//first step
		//if the received video file is larger than two Mega bytes
		//send the first two kbytes of the received video file
		printf("pos1v.__pos > 2*1024*1024\n");
		while(pos2v.__pos < 2*1024*1024)
		{
			n = fread(videoBuf,sizeof(char),BUF_LEN-1,fpv);
			pos2v.__pos += n;
			if(send(sdv,videoBuf,n,0)<0){perror("send");exit(1);}
		}
		//ignore the data in the middle
		fseek(fpv,0,SEEK_END);
		printf("fseek(fpv,0,SEEK_END);\n");
		pos1v.__pos = ftell(fpv);
		printf("pos1v.__pos = ftell(fpv);\n");
	}
	if(pos1v.__pos < 2*1024*1024)
	{//else send the all the received file
	printf("pos1v.__pos < 2*1024*1024\n");
		while(pos2v.__pos < pos1v.__pos)
		{
			n = fread(videoBuf,sizeof(char),BUF_LEN-1,fpv);
			pos2v.__pos += n;
			if(send(sdv,videoBuf,n,0)<0){perror("send");exit(1);}
		}
		pos1v.__pos = pos2v.__pos;
	}
	int fdv = inotify_init();
	if (fdv < 0){perror("inotify init,video\n"); exit(1);}
	int wdv =inotify_add_watch(fdv,"recordvideo.dat",IN_MODIFY);
	
	fd_set fdsv;
	FD_ZERO(&fdsv);
	printf("before while!!!!!!!!!\n");

	while(videoIsOn)
	  {
		FD_SET(fdv, &fdsv);
		printf("before select!!!!!!!!!\n");
		if (select(fdv+1, &fdsv, NULL, NULL, NULL) > 0)
		{//printf("00000000000000000000!!!!!!!!!\n");
			 int len, index = 0;
			 while (((len = read(fd, &videoBuf, sizeof(videoBuf))) < 0) && (errno == EINTR)&& videoIsOn);
			 while ( index < len && videoIsOn)
			 {printf("1111111111111111111!!!!!!!!!\n");
				 struct inotify_event *event = ( struct inotify_event * ) &videoBuf[ index ];
				 printf("1133333333333333333!!!!!!!!!\n");
					 if ( event->mask & IN_MODIFY )
					 {printf("22222222222222222!!!!!!!!!\n");
						 if ( event->mask & IN_ISDIR )
						 {printf("333333333333333333!!!!!!!!!\n");
							 printf( "The directory %s was modified.\n", event->name );
						 }
						 else
						 {printf("4444444444444444444!!!!!!!!!\n");
							 //get new end
							 fseek(fpv,0,SEEK_END);
							 pos2v.__pos = ftell(fpv);//pos2v is the new end
							 printf("55555555555!!!!!!!!!\n");
							 fsetpos(fp,&pos1v);
							 while(pos2v.__pos!=pos1v.__pos && videoIsOn)//output new changes
							{//printf("56666666666666666666!!!!!!!!!\n");
								 n = fread(sendBuf,sizeof(char),BUF_LEN-1,fpv);
								
								 pos1v.__pos += n;
								 if(send(sdv,sendBuf,n,0)<0){perror("send");exit(1);}

							}
						 }
					}//if ( event->mask & IN_MODIFY )
						 index += EVENT_SIZE + event->len;
				 }//while ( index < len )
			 }//if (select
		}//while(videoIsOn)
	( void ) inotify_rm_watch( fdv, wdv );
	( void ) close( fdv);
	fclose(fpv);
	close(sdv);
	printf("thread is over!!!\n");
	return (NULL);
	//second step
	//select if there is any change for the file
}

int intiateMonitor(int iServer, char* uprate, char* downrate)
{

	if(iServer) cld_pid = startServer(uprate, downrate);//start it as server
	else cld_pid = startPeer(uprate, downrate);//start it as peer
	sleep(1);
	printf("start success\n");
	isStart = 1;
	printf("file name is : %s\n",filename);
	fp=fopen(filename,"r+"); // open a file
    fseek(fp,0,SEEK_END);//go to the end of this file
    pos1.__pos = ftell(fp);//record the old end


    //initiate monitoring file
  	fd = inotify_init();
  	if ( fd < 0 ) {perror( "inotify_init" );}
  	wd = inotify_add_watch( fd, filename, IN_MODIFY);//from now on, we can use "fd" in select
}


void killServer()
{
	if((kill(cld_pid,SIGABRT)) < 0) {perror("kill error");exit(1);}//kill failed
	else
	{
		printf(" kill succeed!!\n");
		( void ) inotify_rm_watch( fd, wd );
		( void ) close( fd );
		fclose(fp);
		printf("before return!!\n");
	}
}

//


int main( int argc, char **argv )
{//need two input parameter, the status of the monitor, the ip address of proxy
  programName = argv[0];
  if(argc != 5) usage();
  char *uprate = argv[3];
  char *downrate = argv[4];
  int maxfd;
  videoIsOn = 0;
  char buffer[BUF_LEN];
  char sendBuffer[BUF_LEN];
  int iServer =0;//decide the status
  isStart = 0;
  //decide the status
  if(!strcmp(argv[1], "server")) iServer = 1;

  //get the file to be monitored
  filename = getFileName();
  printf("file name is : %s",filename);
  //std::cout<<"filename is :"<< filename<<std::endl;

  //get the ip address
  struct hostent *h;
  h = gethostbyname(hostname);
  char* myAddress = inet_ntoa(*((struct in_addr *)h->h_addr));
  sprintf(sendBuffer,"%s\r\n",myAddress);

  intiateMonitor(iServer,uprate, downrate);

  //intiate TCP socket
  struct sockaddr_in addr;
  if((sd = socket(AF_INET,SOCK_STREAM,0)) < 0){perror("socket"); exit(1);}
  bzero(&addr,sizeof(addr));
  addr.sin_family = AF_INET;
  addr.sin_port = htons(PORT);
  addr.sin_addr.s_addr = inet_addr(argv[2]);
  int length = sizeof(addr);
  if(connect(sd, (struct sockaddr *)&addr, length) < 0){ perror("connect"); exit(1);}//try to connect


  //send its ip to proxy for datagram type 1
  if(send(sd,sendBuffer,strlen(sendBuffer),0)<0){perror("send");exit(1);}

  //start monitoring
  fd_set fds;
  FD_ZERO(&fds);
  maxfd = fd > sd ? fd: sd;
  printf("wait for event\n");
  //select only two descriptor sd and fd
  FD_SET(fd, &fds);
  FD_SET(sd,&fds);
  for (;;)
  {
	  if (select(maxfd+1, &fds, NULL, NULL, NULL) > 0)
	  {
		  if(FD_ISSET(sd,&fds))
		  {//it is control event from proxy
			  printf("control information\n");
			  int n;
			  if( (n = recv(sd, buffer,1000,0 )) <= 0 )
			  {//the proxy is closed
				  printf("server disconnected\n");
				  //clear
				  close(sd);
				  FD_CLR(sd, &fds);
				  ( void ) inotify_rm_watch( fd, wd );
				  ( void ) close( fd );
				  fclose(fp);//clear

				  exit(1);
				  }
			  else
			  {
				  buffer[n] = 0;
				  if(buffer[0] == 0x06)//close server
				  {//is kill server event;
					  killServer();
					  FD_CLR(fd,&fds);
					  isStart = 0;
					  FD_SET(sd,&fds);
					  maxfd = sd;
					  continue;
					  }
				  if(buffer[0] == 0x07)
				  {//is start server event
					  //printf("type 07\n");
					  if(isStart == 1) ;//already started
					  //do nothings
					  else
					  {
						  printf("restart!!!!!!!!!!!1\n");
						  intiateMonitor(iServer,uprate,downrate);
						  sprintf(sendBuffer,"%s\r\n",myAddress);
						  if(send(sd,sendBuffer,strlen(sendBuffer),0)<0){perror("send");exit(1);}
						  FD_SET(fd, &fds);
						  maxfd = fd > sd ? fd : sd;
						  }
					  }
				  if(buffer[0]==0x08)
				  {//start monitor video event
				  	printf("start the thread\n");
					  pthread_t tid;
					  videoIsOn = 1;
					  pthread_create(&tid,NULL,startVideoMonitor,NULL);
					  FD_SET(fd, &fds);
					  

				  }
				  if(buffer[0] == 0x09)
				  {
				  	printf("it is type 09\n");
					  videoIsOn = 0;
					  FD_SET(fd, &fds);
				  }
				  }
							FD_SET(sd,&fds);//??????????
							printf("finish!!\n");
						}
		  if(FD_ISSET(fd,&fds))
		  {//file-modified event
			  int len, index = 0;
			  while (((len = read(fd, &buffer, sizeof(buffer))) < 0) && (errno == EINTR));
			  while ( index < len )
			  {
				 // printf("11111111\n");
				  struct inotify_event *event = ( struct inotify_event * ) &buffer[ index ];

					  if ( event->mask & IN_MODIFY )
					  {
						  if ( event->mask & IN_ISDIR )
						  {
							  printf( "The directory %s was modified.\n", event->name );
							  }
						  else {
						//	  printf("---------------------------------------------------\n");
						//	  printf("event->name ==filename %d\n",(event->name == filename));
						//	  printf("cmp event->name,filename %d\n",strcmp(event->name, filename));
						//	  printf("event->name:strlen:%d, filenamelenth:%d", strlen(event->name),strlen(filename));
							  // get the changed log and send to proxy server
						//	  printf( "The file %s was modified.\n", event->name );
							  fseek(fp,0,SEEK_END);//find the new end
							  pos2.__pos = 	ftell(fp);//pos2 record the new end
							  fsetpos(fp,&pos1);//set to previous end
							  while(pos2.__pos!=pos1.__pos)//output new changes
								  {
								  char *temp = fgets(buffer,999,fp);
								  pos1.__pos += strlen(temp);
								  //fputs(temp,stdout);//output to stdout
												//distinguish the type of the log
								  char title[3];
								  int n;
								  if(iServer)
								  {//it is server
									 // printf("it is server\n");
									  if (strstr(temp,"rate") != 0)
									  {
										//  printf("data type 0x03\n");
										  title[0]= 0x03;//it is server type 2 0x03
										  }
									  else if(strstr(temp,"0 0 0 0 0") != 0)
									  {
										//  printf("it is 00000\n");
										  continue;
										  }//do not send any information
									  else
									  {
										//  printf("data type 0x02\n");
										  title[0] = 0x02;//it is server type 1 0x02
										//  printf("data type %d\n", title[0]);
										  }
									  }
								  else //it is peer
								{
									//  printf("it is peer\n");
									  if(strstr(temp,"mispkt") != 0)
									  {
										//  printf("data type 0x05\n");
										  title[0] = 0x05;//it is peer type2
										  }
									  else if(strstr(temp,"0 0 0 0 0") != 0)
									  {
										//  printf("it is 00000000000\n");
										  continue;
										  }//do not send any information
									  else
									  {
										//  printf("data type 0x04\n");
										  title[0] = 0x04;// it is peer type 1
										  }
									  }//end else for peer
								  //add the length
								  theLength.length = strlen(temp)+strlen(myAddress);
								  sendBuffer[0]=title[0];
								  if(title[0] == 0x03 || title[0] == 0x02)
								  {
									  theLength.length = strlen(temp);
									  sendBuffer[1] = theLength.byte[1];
									  sendBuffer[2] = theLength.byte[0];
									  sprintf(sendBuffer+3,"%s\r\n",temp);//title;length;content
									  }
								  else
								  {
									  theLength.length = strlen(temp)+strlen(myAddress);
									  sendBuffer[1] = theLength.byte[1];
									  sendBuffer[2] = theLength.byte[0];
									  sprintf(sendBuffer+3,"%s;%s\r\n",myAddress,temp);//title;length;ip;content
									  }
								  //printf("theLength.byte[0]:%d\ntheLength.byte[1]%d\n",theLength.byte[0],theLength.byte[1]);
								 // printf("strlen: %d\n",strlen(sendBuffer));
								  //printf("we sent:%s\n",sendBuffer+3);
								  //printf("the length: %d\n", theLength.length+3);
								  if(send(sd,sendBuffer,theLength.length+5,0)<0){perror("send");exit(1);}
								  //printf("sd:%d\n",sd);
							}//while
						}
					}
				index += EVENT_SIZE + event->len;
				printf("---------------------------------------------------\n");
			}
			  FD_SET(fd, &fds);
			  FD_SET(sd,&fds);
				}
		}
	}
( void ) inotify_rm_watch( fd, wd );
( void ) close( fd );
fclose(fp);
close(sd);
exit( 0 );
}
